pub mod dema;
pub mod dma;
pub mod ema;
pub mod hma;
pub mod lsma;
pub mod rma;
pub mod sma;
pub mod smm;
pub mod smpr;
pub mod swma;
pub mod tema;
pub mod tma;
pub mod trima;
pub mod vidya;
pub mod vwma;
pub mod wma;
pub mod wsma;

use anyhow::ensure;
use anyhow::Result;
use chrono::Utc;
use dema::Dema;
use dma::Dma;
use ema::Ema;
use hma::Hma;
use lsma::Lsma;
use rma::Rma;
use serde::Deserialize;
use serde::Serialize;
use sma::Sma;
use smm::Smm;
use surrealdb::sql::Thing;
use swma::Swma;
use tema::Tema;
use tma::Tma;
use trima::Trima;
use vidya::Vidya;
use vwma::Vwma;
use wma::Wma;

use crate::db::FUDB;
use crate::model::kline::Kline;
use crate::model::kline::KlineMaline;
use crate::model::malines::ma::MAConfig;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct KV<T> {
    pub id: Thing,
    pub value: T,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub(crate) struct KV2 {
    pub id: Thing,
    pub value: KlineMaline,
}

pub async fn compute_n_update_malines(
    klines: &Vec<Kline>,
    config: &Option<MAConfig>,
    only_last: bool
) -> Result<u32> {
    ensure!(klines.len() > 0, "klines is empty");

    let mut updates = 0u32;

    if let Some(fstk) = klines.first() {
        let start = Utc::now();

        let mut malines: Vec<KlineMaline> = Vec::new();
        let ma_config = config.as_ref().cloned().unwrap_or_default();

        let mut dema = Dema::new(&ma_config, &fstk.close).unwrap();
        let mut dma = Dma::new(&ma_config, &fstk.close).unwrap();
        let mut ema = Ema::new(&ma_config, &fstk.close).unwrap();
        let mut hma = Hma::new(&ma_config, &fstk.close).unwrap();
        let mut lsma = Lsma::new(&ma_config, &fstk.close).unwrap();
        let mut lsmav = Lsma::new(&ma_config, &(fstk.volume as f64)).unwrap();
        let mut rma = Rma::new(&ma_config, &fstk.close).unwrap();
        let mut sma = Sma::new(&ma_config, &fstk.close).unwrap();
        let mut smm = Smm::new(&ma_config, &fstk.close).unwrap();
        let mut swma = Swma::new(&ma_config, &fstk.close).unwrap();
        let mut tema = Tema::new(&ma_config, &fstk.close).unwrap();
        let mut tma = Tma::new(&ma_config, &fstk.close).unwrap();
        let mut trima = Trima::new(&ma_config, &fstk.close).unwrap();
        let mut vidya = Vidya::new(&ma_config, &fstk.close).unwrap();
        let mut vwma = Vwma::new(&ma_config, &(fstk.close, fstk.volume as f64)).unwrap();
        let mut wma = Wma::new(&ma_config, &fstk.close).unwrap();

        for (i, kline) in klines.iter().enumerate() {
            if let Some(thing) = &kline.id {
                let dema_v = dema.next(&kline.close);
                let dma_v = dma.next(&kline.close);
                let ema_v = ema.next(&kline.close);
                let hma_v = hma.next(&kline.close);
                let lsma_v = lsma.next(&kline.close);
                let lsmav_v = lsmav.next(&(kline.volume as f64));
                let rma_v = rma.next(&kline.close);
                let sma_v = sma.next(&kline.close);
                let smm_v = smm.next(&kline.close);
                let swma_v = swma.next(&kline.close);
                let tema_v = tema.next(&kline.close);
                let tma_v = tma.next(&kline.close);
                let trima_v = trima.next(&kline.close);
                let vidya_v = vidya.next(&kline.close);
                let vwma_v = vwma.next(&(kline.close, kline.volume as f64));
                let wma_v = wma.next(&kline.close);
                if only_last && i != klines.len() - 1 {
                    continue;
                }
                malines.push(KlineMaline {
                    id: Some(thing.clone()),
                    ema: Some(ema_v),
                    sma: Some(sma_v),
                    lsma: Some(lsma_v),
                    lsmav: Some(lsmav_v),
                    dema: Some(dema_v),
                    dma: Some(dma_v),
                    hma: Some(hma_v),
                    rma: Some(rma_v),
                    smm: Some(smm_v),
                    swma: Some(swma_v),
                    tema: Some(tema_v),
                    tma: Some(tma_v),
                    trima: Some(trima_v),
                    vidya: Some(vidya_v),
                    vwma: Some(vwma_v),
                    wma: Some(wma_v),
                });
                updates += 1;
            }
        }
        let end1 = Utc::now();

        //TODO, use surrealdb Statement For loop
        let query = FUDB.query(
            r#"
                FOR $v IN $malines { 
                    UPDATE $v.id SET malines = $v.value
                }
                "#
        ).bind(("malines", &malines));

        println!("(malines::update_malines) len={} malines[0]={:?}\n", malines.len(), malines[0]);
        let _rs = query.await?;
        // println!("(malines::update_malines) rs= {:?}", _rs);
        let end2 = Utc::now();

        println!(
            "compute_n_update_malines spent time@compute={:?} time@update={:?} for klines.len={}\n",
            end1 - start,
            end2 - end1,
            klines.len()
        );
    }

    Ok(updates)
}

#[cfg(test)]
mod test {
    use chrono::Utc;

    use crate::db;
    use crate::db::FUDB;
    use crate::model::kline::Klt;
    use crate::model::kline::KLINE_COMMON_FIELDS;

    #[tokio::test]
    async fn test_compute_n_update_malines() {
        let start = Utc::now();
        let _ = db::init().await.unwrap();

        // // clean indicators result first
        // let _response = FUDB
        //     .query(
        //         r#"
        //         UPDATE kline
        //         SET ema=NONE, lsma=NONE, lsmav=NONE, sma=NONE
        //         RETURN NONE
        //         "#,
        //     )
        //     .await
        //     .unwrap();

        let klt_vec = vec![
            // Klt::M005,
            // Klt::M015,
            // Klt::M030,
            // Klt::M060,
            Klt::DAY
            // Klt::WEEK,
            // Klt::MONTH,
        ];

        let mut securities = Vec::new();
        if false {
            securities = db::security::select(&None, None, None, None, 0, 1000).await.unwrap();
        } else {
            let ss = db::security::find("SZ.KZZ.128136").await.unwrap();
            securities.push(ss);
        }
        let end1 = Utc::now();
        // TODO: Spent time=TimeDelta { secs: 2378, nanos: 889140000 }
        for security in &securities {
            for klt in &klt_vec {
                let sec_id = &security.id.clone().unwrap().id.to_raw();
                let klines = db::kline
                    ::select(&sec_id, None, Some(klt), Some("klts ASC"), 0, 10000).await
                    .unwrap();

                if klines.len() == 0 {
                    continue;
                }
                let _result = db::malines::compute_n_update_malines(&klines, &None, false).await;
                // let _result = db::malines::compute_n_update_malines(&klines, &None, true).await;

                println!("(test_update_klines_malines_value2) _result={:?}", _result);
                // assert_eq!(_result.is_ok(), true);
            }
        }

        let end2 = Utc::now();
        println!(
            "(test_compute_n_update_malines) Spent time@connect={:?} time@select_update={:?} for securities.len={}\n",
            end1 - start,
            end2 - end1,
            securities.len()
        );

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::DAY),
            Some("klts DESC"),
            0,
            3
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 3 {
                    break;
                }
                // println!(
                //     "[{:02}],{:?} {:?} close:{:.2}=>malines.dema:{:?}",
                //     index,
                //     kline.id.clone().unwrap().id.to_raw(),
                //     kline.klts,
                //     kline.close,
                //     kline.malines.clone().unwrap().dema,
                // );
            }
        }

        // [00],"SZ.KZZ.128136.DAY.20240621" 2024-06-21T00:00:00Z close:113.50=>sma:Some(MA { values: [MAv { period: 5, value: 114.06980000000006 }, MAv { period: 10, value: 113.85010000000004 }, MAv { period: 20, value: 113.11739999999995 }, MAv { period: 30, value: 112.69593333333336 }, MAv { period: 60, value: 110.77361666666648 }, MAv { period: 72, value: 110.22104166666675 }, MAv { period: 89, value: 109.66685393258433 }, MAv { period: 120, value: 109.12114166666649 }, MAv { period: 250, value: 110.43914799999989 }] })
        // [01],"SZ.KZZ.128136.DAY.20240620" 2024-06-20T00:00:00Z close:113.07=>sma:Some(MA { values: [MAv { period: 5, value: 114.24920000000006 }, MAv { period: 10, value: 113.85500000000005 }, MAv { period: 20, value: 113.03284999999994 }, MAv { period: 30, value: 112.60216666666669 }, MAv { period: 60, value: 110.68938333333315 }, MAv { period: 72, value: 110.14036111111119 }, MAv { period: 89, value: 109.56513483146074 }, MAv { period: 120, value: 109.07675833333316 }, MAv { period: 250, value: 110.42993599999988 }] })
        // [02],"SZ.KZZ.128136.DAY.20240619" 2024-06-19T00:00:00Z close:114.20=>sma:Some(MA { values: [MAv { period: 5, value: 114.37960000000005 }, MAv { period: 10, value: 113.87320000000004 }, MAv { period: 20, value: 113.01444999999994 }, MAv { period: 30, value: 112.52323333333335 }, MAv { period: 60, value: 110.60484999999981 }, MAv { period: 72, value: 110.06855555555563 }, MAv { period: 89, value: 109.4688651685394 }, MAv { period: 120, value: 109.0361916666665 }, MAv { period: 250, value: 110.42373199999989 }] })
    }

    #[tokio::test]
    async fn test_update_klines_malines_value() {
        let _ = db::init().await.unwrap();

        // clean indicators result first
        let _response = FUDB.query(
            r#"UPDATE kline SET 
                    ema=NONE, lsma=NONE, sma=NONE
                    RETURN NONE
                "#
        ).await.unwrap();

        let klt_vec = vec![
            // Klt::M005,
            // Klt::M015,
            // Klt::M030,
            // Klt::M060,
            Klt::DAY
            // Klt::WEEK,
            // Klt::MONTH,
        ];

        let start = Utc::now();
        // TODO: 9501.78s too slow,  test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 207 filtered out; finished in 9501.78s
        let securities = db::security::select(&None, None, None, None, 0, 1000).await.unwrap();
        for security in &securities {
            for klt in &klt_vec {
                let sec_id = &security.id.clone().unwrap().id.to_raw();
                let klines = db::kline
                    ::select(&sec_id, None, Some(klt), Some("klts ASC"), 0, 10000).await
                    .unwrap();

                if klines.len() == 0 {
                    continue;
                }
                let _result = db::malines::ema::update_ema(&klines, &None).await;
                let _result = db::malines::lsma::update_lsma(&klines, &None).await;
                let _result = db::malines::sma::update_sma(&klines, &None).await;

                println!("{:?}", _result);
                assert_eq!(_result.is_ok(), true);
            }
        }
        let end = Utc::now();
        println!("Spent time={:?}", end - start);

        let result = db::kline::select(
            "SZ.KZZ.128136",
            None,
            Some(&Klt::M005),
            Some("klts ASC"),
            0,
            100
        ).await;
        if let Ok(klines) = &result {
            for (index, kline) in klines.iter().enumerate() {
                if index > 3 {
                    break;
                }
                // println!(
                //     "[{:02}], close:{:.2}=>malines:{:?}",
                //     index, kline.close, kline.malines,
                // );
            }
        }
    }
}
